#include <EB0/EB0.h>

namespace EB0
{
namespace Entities
{
#pragma region Constructor / Destructor
	// Constructor
	Player::Player() // Calls Entity::Entity()
	{
		// Create an image for the player
		mPlayerImage = Managers::ImageManager::getSingletonPtr()->createImage("graphics/Saffy_Sprite.png");

		// Create a sprite which will use the image
		mPlayerSprite = new sf::Sprite(*mPlayerImage);
		mPlayerSprite->SetCenter(mPlayerSprite->GetSize().x / 2.0f, mPlayerSprite->GetSize().y / 2.0f); // centered origin
	}

	// Destructor
	Player::~Player() // Calls Entity::~Entity()
	{
		// Delete sprite
		if (mPlayerSprite)
		{
			delete mPlayerSprite;
			mPlayerSprite = 0;
		}

		// Delete image
		if (mPlayerImage)
			Managers::ImageManager::getSingletonPtr()->destroyImage(mPlayerImage);
	}
#pragma endregion

#pragma region Attach / Remove
	// Called whenever the entity is attached to a world
	// Used mostly for creating the body
	void Player::onAttach(States::World* worldState)
	{
		// Define the body
		mBodyDef = new b2BodyDef;
		mBodyDef->position.SetZero();

		// Define body shape
		b2PolygonDef shapeDef;
		
		if (mPlayerImage)
			shapeDef.SetAsBox((float32)(mPlayerImage->GetWidth() / TILE_SIZE / 2), (float32)(mPlayerImage->GetHeight() / TILE_SIZE / 2));
		else
			shapeDef.SetAsBox(1.0f, 1.0f);

		shapeDef.density = 1.0f;
		shapeDef.friction = 0.3f;

		// Create the body
		mBody = worldState->getWorld()->CreateBody(mBodyDef);
		mBody->CreateShape(&shapeDef);
		mBody->SetMassFromShapes();

		// Attach window listener for input
		worldState->getWorldLayer()->getWindow()->attachWindowListener(this);
	}

	// Called whenever the entity is removed from a world
	// Used mostly for destroying the body
	void Player::onRemove(States::World* worldState)
	{
		// Remove window listener
		worldState->getWorldLayer()->getWindow()->removeWindowListener(this);

		// Destroy the body
		if (mBody)
		{
			worldState->getWorld()->DestroyBody(mBody);
			mBody = 0;
		}

		// Destroy the body definition
		if (mBodyDef)
		{
			delete mBodyDef;
			mBodyDef = 0;
		}
	}
#pragma endregion

#pragma region Step
	// Step function called for purposes such as AI
	// A pointer to the world state is passed in to access other objects
	void Player::step(States::World* worldState, float dt)
	{
		// Place a cap on the player's speed since we're using impulses
		// We'll normalize the player speed to TILE_SIZE
		if (mBody)
		{
			b2Vec2 velocity = mBody->GetLinearVelocity();
			float32 speed = velocity.Length();

			if (!FLOAT_EQ(speed, 0.0f))
			{
				velocity.x *= TILE_SIZE / speed;
				velocity.y *= TILE_SIZE / speed;
				mBody->SetLinearVelocity(velocity);

				// Disallow rotation
				mBody->SetAngularVelocity(0);
				mBody->SetXForm(mBody->GetWorldCenter(), 0);
			}
		}
	}
#pragma endregion

#pragma region Player Input
	// On Key Press
	void Player::onKeyPress(Window::RenderWindow* window, const sf::Event::KeyEvent& key)
	{
		// Ignore input if the player has no physical body
		if (!mBody)
			return;

		// Apply forces to move the player
		switch (key.Code)
		{
		case sf::Key::Up:
			mBody->ApplyImpulse(b2Vec2(0.0f, -TILE_SIZE), mBody->GetWorldCenter());
			break;

		case sf::Key::Right:
			mBody->ApplyImpulse(b2Vec2(TILE_SIZE, 0.0f), mBody->GetWorldCenter());
			break;

		case sf::Key::Down:
			mBody->ApplyImpulse(b2Vec2(0.0f, TILE_SIZE), mBody->GetWorldCenter());
			break;

		case sf::Key::Left:
			mBody->ApplyImpulse(b2Vec2(-TILE_SIZE, 0.0f), mBody->GetWorldCenter());
			break;

		default:
			break;
		}
	}

	// On Key Release
	void Player::onKeyRelease(Window::RenderWindow* window, const sf::Event::KeyEvent& key)
	{
		// Ignore input if the player has no physical body
		if (!mBody)
			return;

		// Apply forces to move the player
		b2Vec2 velocity = mBody->GetLinearVelocity();

		switch (key.Code)
		{
		case sf::Key::Up:
			velocity.y = 0;
			break;

		case sf::Key::Right:
			velocity.x = 0;
			break;

		case sf::Key::Down:
			velocity.y = 0;
			break;

		case sf::Key::Left:
			velocity.x = 0;
			break;

		default:
			break;
		}

		// Set linear velocity
		mBody->SetLinearVelocity(velocity);
	}
#pragma endregion

#pragma region Render
	// Render
	void Player::Render(sf::RenderTarget& Target) const
	{
		Target.Draw(*mPlayerSprite);
	}
#pragma endregion
} // namespace Entities
}